package com.csx.governance.traffic.adapter.config;

import com.csx.governance.traffic.adapter.inbound.webflux.TrafficSwimLaneWebFilter;
import com.csx.governance.traffic.adapter.inbound.webmvc.TrafficSwimLaneServletFilter;
import com.csx.governance.traffic.adapter.outbound.LoadBalancerInterceptorBeanPostProcessor;
import com.csx.governance.traffic.adapter.outbound.openfeign.CustomFeignRequestInterceptor;
import com.csx.governance.traffic.adapter.outbound.openfeign.TrafficFeignClientWrapProcessor;
import com.csx.governance.traffic.adapter.outbound.openfeign.TrafficServiceInstanceSupplierFeignInterceptor;
import com.csx.governance.traffic.adapter.outbound.resttemplate.TrafficRestTemplateInterceptor;
import com.csx.governance.traffic.adapter.outbound.webclient.TrafficWebClientBuilderBeanPostProcessor;
import com.csx.governance.traffic.core.config.TrafficGovernanceCoreAutoConfiguration;
import com.csx.governance.traffic.core.interceptor.TrafficHandlerInterceptorRunner;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.WebFilter;

/**
 * @author cuisongxu
 * @date 2023/2/23 15:14
 */
@AutoConfigureAfter(TrafficGovernanceCoreAutoConfiguration.class)
@Configuration
@ConditionalOnProperty(value = "traffic.governance.enabled", havingValue = "true", matchIfMissing = true)
@Import(TrafficLoadBalancerConfiguration.class)
@EnableAspectJAutoProxy
public class TrafficGovernanceAdapterAutoConfiguration {

    @Bean
    public TrafficRestTemplateInterceptor trafficRestTemplateInterceptor(@Qualifier("outboundHandlerInterceptorRunner")TrafficHandlerInterceptorRunner handlerInterceptorRunner) {
        return new TrafficRestTemplateInterceptor(handlerInterceptorRunner);
    }

    @ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor")
    @Bean
    public LoadBalancerInterceptorBeanPostProcessor loadBalancerInterceptorBeanPostProcessor() {
        return new LoadBalancerInterceptorBeanPostProcessor();
    }

    @Bean
    public BeanPostProcessor restTemplateInterceptorAdder(TrafficRestTemplateInterceptor trafficRestTemplateInterceptor) {
       return new BeanPostProcessor() {
           @Override
           public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
               if(bean instanceof RestTemplate) {
                   RestTemplate restTemplate = (RestTemplate) bean;
                   restTemplate.getInterceptors().add(trafficRestTemplateInterceptor);
                   return bean;
               }
               return bean;
           }
       };
    }

    @ConditionalOnClass(name = "feign.Client")
    @Bean
    public TrafficFeignClientWrapProcessor trafficFeignClientWrapProcessor(@Qualifier("outboundHandlerInterceptorRunner")TrafficHandlerInterceptorRunner handlerInterceptorRunner) {
        return new TrafficFeignClientWrapProcessor(handlerInterceptorRunner);
    }

    @ConditionalOnClass(name = "feign.Client")
    @Bean
    public CustomFeignRequestInterceptor customFeignRequestInterceptor() {
        return new CustomFeignRequestInterceptor();
    }

    @ConditionalOnClass(name = "feign.Client")
    @Bean
    public TrafficServiceInstanceSupplierFeignInterceptor trafficServiceInstanceSupplierFeignInterceptor(ApplicationContext applicationContext) {
        return new TrafficServiceInstanceSupplierFeignInterceptor((GenericApplicationContext) applicationContext);
    }

    @Configuration
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    static class TrafficSwimLaneWebMvcConfiguration {

        @Bean
        public FilterRegistrationBean<TrafficSwimLaneServletFilter> trafficSwimLaneServletFilter(@Qualifier("inboundHandlerInterceptorRunner") TrafficHandlerInterceptorRunner handlerInterceptorRunner) {
            TrafficSwimLaneServletFilter trafficSwimLaneServletFilter = new TrafficSwimLaneServletFilter(handlerInterceptorRunner);
            FilterRegistrationBean<TrafficSwimLaneServletFilter> filterRegistrationBean = new FilterRegistrationBean<>(trafficSwimLaneServletFilter);
            filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 10000);
            return filterRegistrationBean;
        }
    }

    @Configuration
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    static class TrafficSwimLaneWebFluxConfiguration {

        @Bean
        public WebFilter trafficSwimLaneWebFilter(@Qualifier("inboundHandlerInterceptorRunner")TrafficHandlerInterceptorRunner handlerInterceptorRunner) {
            return new TrafficSwimLaneWebFilter(handlerInterceptorRunner);
        }
    }

    @Bean
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    public TrafficWebClientBuilderBeanPostProcessor trafficWebClientBuilderBeanPostProcessor() {
        return new TrafficWebClientBuilderBeanPostProcessor();
    }


}
